<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script>
      /**2.1. 默认绑定 **/
      //案例一：普通函数调用
      function foo() {
        console.log(this); // window
      }
      foo();

      //案例二：函数调用链（一个函数又调用另外一个函数）
      function test1() {
        console.log(this); // window
        test2();
      }

      function test2() {
        console.log(this); // window
        test3();
      }

      function test3() {
        console.log(this); // window
      }
      test1();

      //案例三：将函数作为参数，传入到另一个函数中
      function foo(func) {
        func();
      }

      function bar() {
        console.log(this); // window
      }

      foo(bar);

      //这里的结果依然是window，为什么呢？
      //原因非常简单，在真正函数调用的位置，并没有进行任何的对象绑定，只是一个独立函数的调用；
      function foo(func) {
        func();
      }

      var obj = {
        name: "why",
        bar: function () {
          console.log(this); // window
        },
      };

      foo(obj.bar);

      /**2.2. 隐式绑定 **/
      // 案例一：通过对象调用函数
      function foo() {
        console.log(this); // obj对象
      }

      var obj = {
        name: "why",
        foo: foo,
      };
      obj.foo();

      // 案例二
      function foo() {
        console.log(this); // obj对象
      }

      var obj1 = {
        name: "obj1",
        foo: foo,
      };

      var obj2 = {
        name: "obj2",
        obj1: obj1,
      };
      obj2.obj1.foo();

      // 案例三：隐式丢失
      function foo() {
        console.log(this);
      }

      var obj1 = {
        name: "obj1",
        foo: foo,
      };
      // 将obj1的foo赋值给bar
      var bar = obj1.foo;
      bar();

      /**2.3. 显示绑定 **/
      //1.call、apply
      function foo() {
        console.log(this);
      }

      foo.call(window); // window
      foo.call({ name: "why" }); // {name: "why"}
      foo.call(123); // Number对象,存放时123

      //2.bind函数
      //方式一
      function foo() {
        console.log(this);
      }

      var obj = {
        name: "why",
      };

      function bind(func, obj) {
        return function () {
          return func.apply(obj, arguments);
        };
      }

      var bar = bind(foo, obj);

      bar(); // obj对象
      bar(); // obj对象
      bar(); // obj对象

      //方式二
      function foo() {
        console.log(this);
      }

      var obj = {
        name: "why",
      };

      var bar = foo.bind(obj);

      bar(); // obj对象
      bar(); // obj对象
      bar(); // obj对象

      //3.内置函数
      //案例一：setTimeout
      setTimeout(function () {
        console.log(this); // window
      }, 1000);

      //案例二：数组的forEach
      var names = ["abc", "cba", "nba"];
      names.forEach(function (item) {
        console.log(this); // 三次window
      });

      //改变this指向
      var names = ["abc", "cba", "nba"];
      var obj = { name: "why" };
      names.forEach(function (item) {
        console.log(this); // 三次obj对象
      }, obj);

      //案例三：div的点击
      var box = document.querySelector(".box");
      box.onclick = function () {
        console.log(this); // box对象
      };

      /**2.4. new绑定 **/
      // 创建Person
      function Person(name) {
        console.log(this); // Person {}
        this.name = name; // Person {name: "why"}
      }

      var p = new Person("why");
      console.log(p);

      /**2.5. new绑定 **/
      //1.默认规则的优先级最低

      //2.显示绑定优先级高于隐式绑定
      function foo() {
        console.log(this);
      }

      var obj1 = {
        name: "obj1",
        foo: foo,
      };

      var obj2 = {
        name: "obj2",
        foo: foo,
      };

      // 隐式绑定
      obj1.foo(); // obj1
      obj2.foo(); // obj2

      // 隐式绑定和显示绑定同时存在
      obj1.foo.call(obj2); // obj2, 说明显示绑定优先级更高

      //3.new绑定优先级高于隐式绑定
      function foo() {
        console.log(this);
      }

      var obj = {
        name: "why",
        foo: foo,
      };

      new obj.foo(); // foo对象, 说明new绑定优先级更高

      //4.默认规则的优先级最低
      //1.new绑定和call、apply是不允许同时使用的，所以不存在谁的优先级更高
      function foo() {
        console.log(this);
      }

      var obj = {
        name: "obj",
      };

      var foo = new foo.call(obj);

      //2.bind后的函数，再进行new
      function foo() {
        console.log(this);
      }

      var obj = {
        name: "obj",
      };

      // var foo = new foo.call(obj);
      var bar = foo.bind(obj);
      var foo = new bar(); // 打印foo, 说明使用的是new绑定


      /** 优先级总结：new绑定 > 显示绑定（bind）> 隐式绑定 > 默认绑定**/
    </script>
  </body>
</html>
